﻿#include <learn/ais/ais_context.h>

namespace
{

enum KeyAction
{
    Release,
    Press,
    Repeat
};

enum KeyCode
{
    SPACE = 32,
    K0 = 48,
    K1,
    K2,
    K3,
    K4,
    K5,
    K6,
    K7,
    K8,
    K9,
    A = 65,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,
    ESC = 256,
    ENTER = 257
};

} // namespace

using namespace xi;
using namespace xi::ais;

AIS_Context::AIS_Context(int width, int height) : m_info(new Info), m_rendererFactory(new RendererFactory)
{
    m_info->width = width;
    m_info->height = height;

    // 场景中初始有一个相机表示观察视角
    auto camera = std::make_shared<AIS_Camera>(width, height);
    add_object(camera);

    m_info->camera = camera->camera();
    m_info->mouse.reset(new AIS_MouseMsg);
    m_info->mouse->toScreen = [&](Vec3f pos) { return m_info->camera->screen(pos); };
    m_info->mouse->toSpace = [&](Vec2f pos) { return m_info->camera->space(pos); };
    m_info->mouse->emit = [&](Vec2f pos) { return m_info->camera->emit(pos); };
}

void AIS_Context::mouse_move_event(Vec2f pos)
{
    // 记录之前的位置
    auto ppos = m_info->mouse->move;

    // 更新鼠标消息
    m_info->mouse->depth = 2.0f;
    m_info->mouse->move = pos;
    m_info->mouse->status = AIS_MouseMsg::Status::Move;
    m_info->mouse->obj = nullptr;

    if (m_action == Action3d::Rotate)
    {
        // 注意要让画面朝 dv 方向移动，相机要朝 -dv 方向移动
        m_info->camera->rotate((ppos.x() - pos.x()) * m_angleRate, (ppos.y() - pos.y()) * m_angleRate);
    }
    else if (m_action == Action3d::Translate)
    {
        // 注意要让画面朝 dv 方向移动，相机要朝 -dv 方向移动
        m_info->camera->translate(Vec2f{ppos.x() - pos.x(), ppos.y() - pos.y()});
    }
    else
    {
        // 如果当前正在选中物体，就移动物体
        if (m_info->pressed)
        {
            // 处理移动消息
            auto msg = std::make_shared<AIS_MoveMsg>();
            msg->pos = m_info->camera->space(pos);
            msg->dir = m_info->camera->emit(pos);
            m_info->pressed->handle_msg(msg);
        }
        else
        {
            // 清除悬停物体
            m_info->hang = nullptr;

            // 进行碰撞检测
            for (auto obj : m_objects)
                obj->handle_msg(m_info->mouse);

            // 获得悬停物体
            m_info->hang = m_info->mouse->obj;
            if (m_info->hang)
                m_info->hang->set_status(AIS_Object::Status::Hovered);
        }
    }
}

void AIS_Context::mouse_press_event(Vec2f pos, AIS_MouseMsg::Button button)
{
    // 更新鼠标消息
    m_info->mouse->depth = 2.0f;
    m_info->mouse->button = button;
    m_info->mouse->status = AIS_MouseMsg::Status::Press;
    m_info->mouse->obj = nullptr;

    if (button == AIS_MouseMsg::Button::Left)
    {
        // 记录按键
        m_info->mouse->press = pos;
        m_info->pressed = nullptr;

        // 进行碰撞检测
        for (auto obj : m_objects)
            obj->handle_msg(m_info->mouse);

        // 获得按下物体
        m_info->pressed = m_info->mouse->obj;
        if (m_info->pressed)
        {
            m_info->pressed->set_status(AIS_Object::Status::Pressed);
            m_info->selected.push_back(m_info->pressed);
        }
    }
    else if (button == AIS_MouseMsg::Button::Right)
    {
        m_action = Action3d::Translate;
    }
    else if (button == AIS_MouseMsg::Button::Middle)
    {
        if (m_dimension == Dimension::Axis3d)
            m_action = Action3d::Rotate;
    }
}

void AIS_Context::mouse_release_event(Vec2f pos)
{
    // 解除按键
    m_action = Action3d::None;
    m_info->mouse->depth = 2.0f;
    m_info->mouse->status = AIS_MouseMsg::Status::Release;
    m_info->mouse->obj = nullptr;

    // 只有被按压的物体处理释放消息
    if (m_info->pressed)
    {
        m_info->pressed->handle_msg(m_info->mouse);
        m_info->pressed = nullptr;
    }
}

void AIS_Context::mouse_wheel_event(Vec2f angleDelta)
{
    if (m_action == Action3d::None)
    {
        m_action = Action3d::Scale;

        // 沿着屏幕位置缩放
        m_info->camera->translate(m_info->mouse->move, -angleDelta.y() * m_scaleRate);

        m_action = Action3d::None;
    }
}

void AIS_Context::key_event(int key, int scancode, int action, int mods)
{
    if (action == KeyAction::Press)
    {
        switch (key)
        {
        case KeyCode::K0:
            m_info->camera->set_center(Vec3f{0.0f, 0.0f, 0.0f});
            break;
        case KeyCode::K1:
            m_info->camera->upright_view();
            break;
        case KeyCode::K3:
            m_info->camera->right_view();
            break;
        case KeyCode::K7:
            m_info->camera->top_view();
            break;
        case KeyCode::K9:
            m_info->camera->bottom_view();
            break;
        case KeyCode::A:
            if (m_dimension == Dimension::Axis3d)
                m_dimension = Dimension::Axis2d;
            else if (m_dimension == Dimension::Axis2d)
                m_dimension = Dimension::Axis3d;
            break;
        default:
            break;
        }
    }
    else if (action == KeyAction::Release)
    {
    }
    else
    {
    }
}

void AIS_Context::resize_event(int width, int height)
{
    m_info->width = width;
    m_info->height = height;
    m_info->camera->resize(width, height);
}

void AIS_Context::paint_event()
{
    auto msg = std::make_shared<AIS_PaintMsg>();
    for (auto obj : m_objects)
        obj->handle_msg(msg);
}

void AIS_Context::add_object(std::shared_ptr<AIS_Object> object)
{
    // 初始化渲染器，然后刷新
    auto init = std::make_shared<AIS_InitMsg>();
    auto update = std::make_shared<AIS_UpdateMsg>();
    init->factory = m_rendererFactory;

    object->handle_msg(init);
    object->handle_msg(update);
    m_objects.push_back(object);
}

std::shared_ptr<RendererFactory> AIS_Context::renderer_factory() const noexcept
{
    return m_rendererFactory;
}

AIS_Context::Info *AIS_Context::info() const noexcept
{
    return m_info.get();
}